docs(skills): judge each of the ten governed fences that shadowed a published type - #8427
Conversation
…ublished type
Five fences stop keeping a private copy of a published `@object-ui` type, four
keep a deliberately simplified teaching copy and now say so, and one is a prose
repair for a type that is not on the published surface at all. Per-fence
judgement, never a sweep: a guide's fence is often a short view of a large type,
and converting those wholesale would make several guides worse.
Import (the copy was already the whole type, or was wrong about it):
auth-permissions.md AuthUser — the copy still taught the `roles` member
ADR-0090 D3 renamed to `positions`
data-integration.md QueryParams — the copy invented an index signature the
published type does not have
data-integration.md QueryResult — the copy was the whole type, member for
member
plugin-development.md ComponentInput, ComponentInputControlType
— the copy declared `label`,
`defaultValue` and `advanced` writable
where all three are `never` tombstones,
and carried no `of`
Derive under `Pick` (a short shape anchored to the real one):
plugin-development.md FieldWidgetComponentProps — the seven props an author
writes, out of a much larger closed type
Keep the teaching copy, and say so (published type too large to open with):
architecture.md BaseSchema (21 members + index signature)
protocol.md BaseSchema
data-integration.md DataSource (38 members)
console-development.md MetadataResourceConfig (28 members)
Prose only, no import exists to write:
console-development.md NavigationContextType — internal to `packages/app-shell`
and not re-exported from its entry
Both shrink-only ledgers in `scripts/check-skill-examples.mjs` shrink to empty
in this same commit, because a row that outlives its fence fails as STALE:
the four `KNOWN_SHADOWED_PUBLISHED_TYPES` rows whose three fences now import,
and the last `KNOWN_BARE_ANY_EXAMPLES` row, whose `any` went with the copy that
carried it. No assertion, threshold or list shape changed.
Claude-Session: https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX
… said 28 The paragraph above the MetadataResourceConfig fence gave two different totals for the same interface in the same breath: "the 22 fields you reach for most out of the 28 the interface declares ... registry.ts documents all 27 field by field", with six unshown fields named (22 + 6 = 28). The 27 was a stale number from an earlier count, left behind when the AST measurement corrected it. Re-measured on this branch: the interface has 28 members and registry.ts carries a doc comment on every one of them, so "all 28 field by field" is the reading. Verified by walking the members with the TypeScript AST and checking each one's leading trivia — 28 documented, 0 undocumented. Claude-Session: https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX
|
ACCEPT — flight M, PR #8427 at head Own readings (review worktree detached at
Review checklist:
Implemented-by: os-dev subagent 维护者速读改了什么: 六个技能指南里的十处代码围栏逐处判定,不是扫荡:四处改成直接 为什么改: 三份副本在教错: 风险与代价(含回滚): 只动文档与一个仓库级门禁脚本的欠账行,不碰任何发布包源码,changeset 门禁自判「不欠」。三处 MARKED 围栏现在真会被编译,门禁绿;已发布技能包净增 84 行(+1.9%),增的就是「保留副本就要说明白」那些话。一处申报的偏差:修第 6 处围栏连带让另一张表(bare 席位意见: 复核通过,四处「保留副本」的取舍席位认可(各自的成员数与理由都在正文表里);建议合并。 你要做的: 手动合并本 PR(受管面,⛔ 不走队列、不挂 auto-merge);合并即关 objectui#8335。是否合并? Generated by Claude Code |
Fixes #8335
The fence-repair half of objectui#7646. Ten governed teaching fences re-declared a name the built
@object-uisurface publishes; each one gets a judgement here, not a sweep.The ruling this executes
No maintainer ruling exists on objectui#8335. objectui#7646's triage ruling, quoted from the card body and kept in the original wording:
Both halves are honoured: ten judgements, none of them mechanical, and every ledger row that died did so in the one commit that repaired its fence.
The ten judgements
guides/architecture.md:20BaseSchemaguides/auth-permissions.md:59AuthUserrolesguides/data-integration.md:27DataSourceguides/data-integration.md:63QueryParamsguides/data-integration.md:81QueryResultguides/plugin-development.md:92ComponentInput,ComponentInputControlTypeguides/plugin-development.md:275FieldWidgetComponentPropsPickkeeps it short AND anchoredrules/protocol.md:48BaseSchemaobjectui-contributor/guides/console-development.md:154MetadataResourceConfigPickof 22 keys reads worse than the excerptobjectui-contributor/guides/console-development.md:279NavigationContextTypeSplit, counted in FENCES: 4 import · 1 derive · 4 keep-and-say-so · 1 prose — imports are rows 2, 4, 5 and 6; keeps are rows 1, 3, 8 and 9. Row 6 is one fence carrying two names, which is why the ledger held four ROWS over three MARKED fences; it does not make row 6 two fences, and the
--measurereading below agrees — four surviving keep fences.Fence 1 —
BaseSchemainarchitecture.md, KEEP[key: string]: any. This is the protocol intro; a reader who met all 21 here learns nothing about the shape.propsandeventsare NOT declared members, they arrive through the index signature. A reader who imports the type and cannot find them now knows why instead of concluding the guide lies.Fence 2 —
AuthUserinauth-permissions.md, IMPORTpackages/auth/dist/types.d.ts: the copy taughtroles, which the published type has not had since objectui#5424, and omittedpositionsandtenantId— the two members an authorization decision actually reads.AuthUserextends the spec's principal precisely so a new spec field lands the day the spec adds it; a private copy in the guide defeated that for the guide's readers.Fence 3 —
DataSourceindata-integration.md, KEEP.d.ts: 38 members, 6 required and 32 optional. The guide's existing prose already claimed exactly those numbers and is still exact.saveView, a genericexecute).Fence 4 —
QueryParamsindata-integration.md, IMPORT[key: string]: anywith the comment "why an unprefixedlimittype-checks — and is then dropped". Measured: the published type has NO index signature, so that claim is false; an unprefixedlimitis a compile error.Fence 5 —
QueryResultindata-integration.md, IMPORTdataand NOTrecords, but now as a compiled read of the imported type rather than as a comment on a copy.Fence 6 —
ComponentInputandComponentInputControlTypeinplugin-development.md, IMPORTlabel,defaultValueandadvancedwere declared WRITABLE where all three areneverretirement tombstones, andofwas missing entirely. Its own comment said "five tombstones" where the type carries eight.Pickso a tombstone deleted upstream stops compiling here.Fence 7 —
FieldWidgetComponentPropsinplugin-development.md, DERIVEPickwhere the real type belongs.Fence 8 —
BaseSchemainrules/protocol.md, KEEPproperties,propsandeventsare absent from the published member list, which is exactly what the two rules underneath it are about.Fence 9 —
MetadataResourceConfiginconsole-development.md, KEEP.d.ts: 28 members, of which the fence shows 22, every one correctly spelled and typed. This is an internal contributor guide whose reader has the repo open.Pickof 22 keys is longer and less informative than the excerpt, and would drop the shapes (listColumns,createDerive) that make the section usable.Fence 10 —
NavigationContextTypeinconsole-development.md, PROSE@object-ui/app-shell'sexportsmap has only.and./styles.css, with the entry re-exportinguseNavigationContextbut not this type.Ledger diff
Both shrink-only ledgers in
scripts/check-skill-examples.mjsshrink to EMPTY, in the same commit as the fences they described. ⛔ No assertion, threshold or list shape changed — the self-test's 59 cases, including the shadowing ledger driven in both directions, pass over the empty ledger.KNOWN_SHADOWED_PUBLISHED_TYPES: 4 rows retired, 0 kept, 0 added.auth-permissions.md:59 AuthUserdata-integration.md:81 QueryResultplugin-development.md:92 ComponentInputplugin-development.md:92 ComponentInputControlTypeNo row is kept, so no row's reason needed rewriting, and after this PR no row anywhere in the gate still names objectui#8335 as its owner — verified by grep, 0 hits.
KNOWN_BARE_ANY_EXAMPLES: 1 row retired — and this is the one deviation from the dispatch's file surface, declared rather than slipped in.The dispatch authorised touching the gate ONLY to retire a
KNOWN_SHADOWED_PUBLISHED_TYPESrow. Repairing fence 6 also kills the gate's last bare-anyrow,plugin-development.md:92 property defaultValue, because theanyit names lived in the copy that fence 6 no longer keeps. A stale row on a shrink-only list is a hard red (state.bareAnyStale.lengthabove zero is in the run's failure condition), so leaving it is not an option, and the same list's own header records the same operation being done for the same reason on three earlier rows: "the rows went in the same commit as the guide edit, because a row whose red is gone fails as STALE".Its stated reason had also gone out of date underneath it. The row said the guide was FAITHFUL prose because
ComponentInput.defaultValue"really isany" in the platform type. It is not: the platform type carries it as an ADR-0049 retirement tombstone. So the row's own rationale had inverted — the guide'sanywas rot after all, and the honest fix WAS to the guide.The five premises, as measured
typescriptfenceb38014e8; each local declaration read and matched against the built.d.ts--measure, and@object-ui/app-shell'sexportsmap has no path that reaches it4 row(s). Every row reads "repair owned by objectui#8335". Nothing else about the premise moved.skills/or.claude/skills/. #8384's file list read directly —content/docs/guide/schema-overview.md,content/docs/utilities/data-objectstack.mdx,lychee.tomlonly.A separate re-derivation worth recording: the gate's own
--measureonb38014e8prints 10 re-declarations across 9 fences for the two governed roots — the card's eleven named types minusNavigationContextType, which the built-surface oracle correctly does not carry. The card's census summary said "skills/ 8 · .claude/skills/ 2", which counts its own table ROWS rather than declaration/name pairs.Gates
Every command below was run in this worktree; heavy runs went through the container's shared verify lock. The verdict line quoted is the gate's own, never a bare exit code.
pnpm check:skill-examplespnpm check:skill-eval-tokenspnpm check:skills-pathspnpm check:control-bytesnode scripts/check-skill-examples.mjs --self-testpnpm exec vitest runon the three skills gate suitespnpm type-check:scriptspnpm lint:rootnode scripts/check-changeset-presence.mjsnode scripts/check-governed-queue-guard.mjs --teston all 7 touched pathsThe three
check:skill-examplescounts, before and afterMarked:Semantic phase:Shadowed types:ledger rowsBare any:moved with them — before: 1 finding, 1 declared, ledger 1 row. After: 0 findings, ledger 0 rows, 0 stale.⛔ No new fence was marked. The marked population is byte-for-byte the same 13, and the floor is untouched — growing the judged population is a gate-design decision this card puts out of scope, and the dispatch names it as a thing not to do for its own sake.
The unmarked repairs were still compiled, without marking them
--measurejudges every candidate fence, marked or not, so it verifies the two unmarked fences that now assert an import. Per-fence verdicts after the repair:auth-permissions.md:59— marked — pass (shadow note gone)data-integration.md:68(QueryParams) — unmarked — passdata-integration.md:98(QueryResult) — marked — passplugin-development.md:92(ComponentInput) — marked — passplugin-development.md:296(FieldWidgetComponentPropsderive) — unmarked — passThe whole-corpus reading moved with them:
Starting population — ts: 15/121 passbefore,17/121 passafter; the shadowing would-be population fell from 10 to 4, and the four survivors are exactly the four keep-the-teaching-copy fences, all unmarked and all ungated.Lint narrowing, with its three evidences
pnpm linthere isturbo run lintover the packages, and this diff touches no package. The population that does contain a changed file ispnpm lint:root, and it was run in full — exit 0, no narrowing claimed for it. For the six markdown files:eslint.config.jsis scoped**/*.{ts,tsx}or narrower, and eslint's own--format jsonlabels all six markdown files "File ignored because no matching configuration was supplied".--format jsonon the changed files — 7 paths in, 6 outside the population, 1 (scripts/check-skill-examples.mjs) inside it and clean at 0 errors / 0 warnings; process exit 0.eslint.config.jsenables no type-aware linting (zero occurrences ofprojectService,parserOptions.projector aprojectkey), so no file's verdict can depend on another file's contents, and a diff confined to these seven paths cannot move any untouched file's result.Changeset
Applied rule: objectui's changeset obligation is triggered by published executable source of a release-group package, or by a move in one of the eight publish-contract manifest fields. This diff has neither — six markdown guides and one repo-level gate script.
node scripts/check-changeset-presence.mjsis the authority and says so in its own words: "Compared the working tree with b38014e (merge-base with origin/main): 7 file(s) changed, 0 of them published source of a package the release covers, 0 of them a manifest whose published contract moved, 0 under a package changesets ignores, 0 changeset(s) added. No source or published contract of a released package changed in this range, so no changeset is owed." ⛔ Noskip-changesetlabel applied: in this repository that label reads nothing and exempts nothing, and the gate's verdict line is the authority.Skills size readings
Two readings, as the dev contract requires for a diff touching the published
skills/tree. Lines, since this repository defines no token count for skills.skills/skills/objectui/SKILL.mdguides/architecture.mdguides/auth-permissions.mdguides/data-integration.mdguides/plugin-development.mdrules/protocol.md.claude/skills/objectui-contributor/guides/console-development.md(internal tree, not the published bundle)The growth is the deliverable rather than overhead: four of the ten judgements ARE "keep the teaching copy and say so", and saying so is prose. The four imports and the one derive also had to move the member facts their deleted copies carried into prose next to the fence. No mechanical ceiling exists for this tree in this repository — there is no skills size or token ratchet script — so nothing was blown and none was raised.
Serial
git fetch origin main && git merge-tree --write-tree --name-only origin/main HEADat the head of this branch, read twice asmainmoved during the run:origin/mainatfadf6cd7: exit 0, a single tree OID (a2c28c7d), no conflicted-path list.origin/mainat0203a29e: exit 0, a single tree OID (06b980b3), no conflicted-path list.Branch point was
b38014e8. Seventeen commits landed onmainin between and none touches a path underskills/,.claude/skills/orscripts/check-skill-*, so there is no semantic overlap either. One of them is a breaking change to a package this PR's fences import (cb847fd6, dropping theCloudnamespace re-export from@object-ui/types' root entry), so every name imported by a repaired fence was re-checked against that tree:QueryParams,QueryResult,ComponentInputandComponentInputControlTypeare still exported frompackages/types/src,AuthUserfrompackages/auth/src/types.ts, andFieldWidgetComponentPropsfrompackages/fields/src/widgets/types.ts. No fold was needed and none was done; the merge queue rebuilds onmainregardless.验收备注
PermissionProviderexample in the same guide passesuserRolesfromuser?.roles, a member the publishedAuthUserno longer declares. Measured with a probe against the built.d.ts: the guide's line iserror TS2740and thepositionscontrol line is clean. ⛔ Deliberately NOT repaired here: it is a fence that READS a retired member, not one that re-declares a published type, so it is a different defect class and outside this card's ten. 承接者:objectui#8393 itself, in the skills lane.data-integration.md's DataSource prose claims "6 required, 32 optional" and it is still exactly right; re-measured by AST on the built.d.ts(38 members total). Recorded so the next reader does not re-derive it.维护者速读(草稿)
改了什么
六个技能指南文件里的十处代码围栏,每一处都单独判了一次:四处改成直接
import已发布的类型(第 2、4、5、6 处)、一处用Pick从真类型派生(第 7 处)、四处保留简化教学副本(第 1、3、8、9 处)并在正文里写明"这是简化副本、真类型在哪、没有任何东西在校验它"、一处(NavigationContextType)只改正文,因为那个类型压根没有对外发布、读者写不出 import。同一个提交里,门禁脚本的两张"只准缩"的欠账表都清空了 —— 表里的行描述的就是被修好的那几处围栏,行活得比围栏久就会以 STALE 报红。门禁的断言、阈值、表的形状都没动。为什么改
这些围栏自己抄了一份平台已经发布的类型,抄完就再也不会跟着变。实测出来的后果不是理论上的:
AuthUser那份副本还在教roles这个键,而它在 2026-08-22 的裁决里就被改名成positions了;ComponentInput那份把label、defaultValue、advanced三个键写成"可写",而真类型里这三个都是never墓碑 —— 也就是说,照着这份指南写组件清单的 AI,会写出编译器和 Zod 都会当场拒收的键;QueryParams那份凭空多了一个索引签名,还配了一句"所以不带前缀的limit能通过类型检查"的解释,而真类型没有索引签名,那句话是反的。这三处都是"文档看着绿、其实一直在教错"的形态。风险与代价(含回滚)
风险很低:只动文档与一个仓库级门禁脚本,没有任何一个发布包的源码被碰,
check-changeset-presence自己判定"不欠 changeset"。三处 MARKED 围栏现在真的会被编译,门禁跑绿;两处未标记的围栏也用--measure单独验过、都是 pass。代价是已发布技能包净增 84 行(4500 → 4584,+1.9%),这部分增长本身就是裁决要求的"保留副本就要说明白"。回滚成本等于零:git revert一个提交即可,不涉及任何数据迁移、不涉及任何运行时行为。唯一需要您知道的偏差:派发单只授权我为了退掉"影子类型"那张表去动门禁脚本,但修好第 6 处围栏会连带让另一张表(bareany)的最后一行变成 STALE、直接把门禁打红,所以那一行也在同一个提交里退了 —— 而且它自己写的理由已经过期了(它说平台类型里defaultValue"确实是any",实际上早就是墓碑)。席位意见
你要做的
这是受管面(
skills/**与.claude/**),按规矩停在 draft 等您合并,我不会翻 ready、不会入队、不会自己批准。请看一眼上面十条判断里"保留教学副本"的那四条(第 1、3、8、9 处)是不是您想要的取舍 —— 如果同意,回一个词就行:合并。Drafted by Claude Code; this pull request's session is
https://claude.ai/code/session_018dxq7YqsLDMeZDZ5AzsgJX. Attribution is prose with the session URL as a code span rather than a footer block, because a body edit through the API does not carry a footer block through.